﻿@model PaymentInfoModel

@{
    //container names
    var paypalButtonContainer = $"paypal-button-container-{Model.ProductId}".TrimEnd('-');
    var paypalApmContainer = "paypal-apm-container";
    var paypalCardContainer = "paypal-card-container";
    var paypalApplePayContainer = "paypal-applepay-container";
    var paypalGooglePayContainer = "paypal-googlepay-container";
    var paypalMessagesContainer = "paypal-messages-container";

    var pageType = Model.Placement switch
    {
        ButtonPlacement.Cart => "cart",
        ButtonPlacement.Product => "product-details",
        ButtonPlacement.PaymentMethod => "checkout",
        _ => null
    };
}

<link rel="stylesheet" href="~/Plugins/Payments.PayPalCommerce/Content/styles.css" />
<script asp-location="Auto" src="@Model.Script.Url" data-page-type="@pageType" data-client-token="@Model.Script.ClientToken" data-user-id-token="@Model.Script.UserToken" data-partner-attribution-id="@PayPalCommerceDefaults.PartnerHeader.Value"></script>
@if (settings.UseApplePay && Model.Placement != ButtonPlacement.Product)
{
    <script asp-location="Auto" src="@PayPalCommerceDefaults.ApplePayScriptUrl"></script>
}
@if (settings.UseGooglePay)
{
    <script asp-location="Auto" src="@PayPalCommerceDefaults.GooglePayScriptUrl"></script>
}
@if (settings.UseSandbox || settings.ConfiguratorSupported)
{
    <script asp-location="Auto" src="@PayPalCommerceDefaults.MerchantConfiguratorScriptUrl" defer></script>
}

<div id="@paypalButtonContainer" class="paypal-button-container"></div>
@if (Model.Placement == ButtonPlacement.PaymentMethod && settings.UseAlternativePayments)
{
    <div id="@paypalApmContainer" class="paypal-apm-container"></div>
}
@if (Model.Placement == ButtonPlacement.PaymentMethod && settings.UseCardFields)
{
    <div id="@paypalCardContainer" class="paypal-card-container">
        <div id="saved-cards-container" style="display: none">
            <select id="saved-cards" name="saved-cards" class="card_field" onchange="changeSavedCard(this.value);"></select>
        </div>
        <div id="card-number-field-container"></div>
        <div id="card-expiry-field-container"></div>
        <div id="card-cvv-field-container"></div>
        <div id="card-name-field-container"></div>
        <div id="card-vault" class="card-vault card_field" style="display: none">
            <input type="checkbox" id="vault" name="vault" />
            <label>@T("Plugins.Payments.PayPalCommerce.Card.Save")</label>
        </div>
        <button id="card-field-submit-button" type="button" class="button-1 confirm-order-next-step-button paypal-button" style="display: none">
            @T("Plugins.Payments.PayPalCommerce.Card.Button")
        </button>
    </div>
}
@if (settings.UseApplePay && Model.Placement != ButtonPlacement.Product)
{
    <div id="@paypalApplePayContainer" class="paypal-applepay-container"></div>
}
@if (settings.UseGooglePay)
{
    <div id="@paypalGooglePayContainer" class="paypal-googlepay-container"></div>
}
@if (Model.Placement == ButtonPlacement.Product && (settings.UseSandbox || settings.ConfiguratorSupported))
{
    <div id="@paypalMessagesContainer" class="paypal-messages-container"></div>
}

<script asp-location="Footer">
    // define global params
    var waitingPayPal = false;
    var isCartPage = @((Model.Placement == ButtonPlacement.Cart).ToString().ToLower());
    var isProductPage = @((Model.Placement == ButtonPlacement.Product).ToString().ToLower());
    var isPaymentMethodPage = @((Model.Placement == ButtonPlacement.PaymentMethod).ToString().ToLower());
    var isApm = false;
    var paypalButtonContainer = null;
    var applePayConfig = null;
    var googlePayClient = null;
    var googlePayConfig = null;

    $(function () {
        if (isCartPage) {
            //hide buttons in shopping cart according to the settings
            if ($('#checkout').length > 0) {
                $('#checkout').after($('#@paypalButtonContainer'));
                var hideCheckoutButton = @((settings.HideCheckoutButton).ToString().ToLower());
                if (hideCheckoutButton) {
                    $('#checkout').hide();
                }
            } else {
                $('#@paypalButtonContainer').hide();
            }
        }

        var noShippingChange = isPaymentMethodPage || @((!Model.Cart.IsShippable).ToString().ToLower());

        //render PayPal buttons
        paypalButtonContainer = paypal.Buttons({
            onInit: initCallback,
            onClick: clickCallback,
            createOrder: createOrderCallback,
            onApprove: approveCallback,
            onCancel: cancelCallback,
            onError: errorCallback,
            onShippingAddressChange: noShippingChange ? null : shippingAddressChangeCallback,
            onShippingOptionsChange: noShippingChange ? null : shippingOptionsChangeCallback,
            style: {
                layout: '@(Model.Placement == ButtonPlacement.Product ? "horizontal" : settings.StyleLayout)',
                color: '@settings.StyleColor',
                shape: '@settings.StyleShape',
                label: '@settings.StyleLabel',
                tagline: '@(Model.Placement == ButtonPlacement.Product || settings.StyleLayout == "horizontal" ? settings.StyleTagline : string.Empty)'
            }
        });
        paypalButtonContainer.render('#@paypalButtonContainer')

        //render Card Fields (if available)
        if ($('#@paypalCardContainer').length > 0) {
            var cardFields = paypal.CardFields({
                createOrder: createOrderCallback,
                onApprove: approveCallback,
                onCancel: cancelCallback,
                onError: errorCallback
            });

            if (cardFields.isEligible()) {
                cardFields.NumberField().render('#card-number-field-container');
                cardFields.CVVField().render('#card-cvv-field-container');
                cardFields.ExpiryField().render('#card-expiry-field-container');
                cardFields.NameField().render('#card-name-field-container');
                $('#@paypalCardContainer').before('<div id="paypal-divider" class="divider"><span>OR</span></div>');
                $('#card-field-submit-button').show();
                var vaultEnabled = @((!string.IsNullOrEmpty(Model.Script.UserToken)).ToString().ToLower());
                if (vaultEnabled) {
                    $('#card-vault').show();
                }

                //try to get previously saved cards
                var postData = {
                    placement: '@((int)Model.Placement)'
                };
                addAntiForgeryToken(postData);
                $.ajax({
                    async: true,
                    type: 'POST',
                    url: '@(Url.Action("GetSavedCards", "PayPalCommercePublic"))',
                    data: postData,
                    success: function (data, textStatus, jqXHR) {
                        if (data.error) {
                            displayBarNotification(data.error, 'error', 0);
                            $('#saved-cards-container').hide();
                        } else if (data.cards) {
                            //display cards as a list
                            $('#saved-cards').html('');
                            $('#saved-cards').append($('<option></option>').val('0').html('@T("Plugins.Payments.PayPalCommerce.Card.New")'));
                            $.each(data.cards, function (i, card) {
                                $('#saved-cards').append($('<option></option>').val(card.id).html(card.label));
                            });
                            $('#saved-cards').val(data.defaultId);
                            $('#saved-cards-container').show();
                            changeSavedCard($('#saved-cards').val());
                        } else {
                            $('#saved-cards-container').hide();
                        }
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        if (errorThrown) {
                            displayBarNotification(errorThrown, 'error', 0);
                        } else {
                            displayBarNotification(textStatus, 'error', 0);
                        }
                        $('#saved-cards').hide();
                    }
                });
            } else {
                $('#@paypalCardContainer').hide();
            }

            //'Pay by card' button click handler
            $('#card-field-submit-button').click(function () {
                if (waitingPayPal !== false)
                    return;

                $('#card-field-submit-button').prop('disabled', true);
                $('#card-field-submit-button').addClass('paypal-button-disabled');
                var cardId = $('#saved-cards').val();
                if (cardId > 0) {
                    var status = '';
                    var payerAction = '';

                    //try to create order, when customer selects one of the saved cards
                    var orderId = createOrderCallback({ paymentSource: 'card', cardId: cardId });

                    displayAjaxLoading(true);
                    waitingPayPal = true;

                    //then check whether the customer action is required (3D Secure cases)
                    var postData = {
                        placement: '@((int)Model.Placement)',
                        orderId: orderId
                    };
                    addAntiForgeryToken(postData);
                    $.ajax({
                        async: false,
                        type: 'POST',
                        url: '@(Url.Action("GetOrderStatus", "PayPalCommercePublic"))',
                        data: postData,
                        success: function (data, textStatus, jqXHR) {
                            if (data.error) {
                                displayBarNotification(data.error, 'error', 0);
                            } else {
                                status = data.status;
                                payerAction = data.payerAction;
                            }
                        },
                        complete: function (jqXHR, textStatus) {
                            displayAjaxLoading(false);
                            waitingPayPal = false;
                        },
                        error: function (jqXHR, textStatus, errorThrown) {
                            if (errorThrown) {
                                displayBarNotification(errorThrown, 'error', 0);
                            } else {
                                displayBarNotification(textStatus, 'error', 0);
                            }
                        }
                    });

                    //approve the order immediately or redirect customer to resolve 3DS contingency
                    if (status === 'APPROVED' || status === 'COMPLETED') {
                        approveCallback({ orderID: orderId, liabilityShift: 'YES' });
                    } else if (status === 'CREATED' || status === 'PAYER_ACTION_REQUIRED') {
                        if (payerAction) {
                            setLocation(payerAction);
                        } else {
                            displayBarNotification('Order is ' + status, 'error', 0)
                        }
                    }
                } else if (cardFields.isEligible()) {
                    //let the script handle card fields, when customer enters payment info
                    cardFields.submit().catch((error) => {
                        displayBarNotification(error.message, 'error', 0)
                    });
                }
                $('#card-field-submit-button').prop('disabled', false);
                $('#card-field-submit-button').removeClass('paypal-button-disabled');
            });
        }

        //generate Pay Later message element on product pages (on the cart and checkout pages we generate it in a separate view component)
        if (isProductPage && $('#@paypalMessagesContainer').length > 0) {
            var messages = window.merchantConfigurators?.generateMessagingCodeSnippet({
                messageConfig: @Html.Raw(Model.MessagesModel.Config),
                productPrice: '@Model.MessagesModel.Amount',
            });
            $('#@paypalMessagesContainer').html(messages);
            $('.add-to-cart-panel').before($('#@paypalMessagesContainer'));
        }

        //render Apple Pay button
        var useApplePay = @((settings.UseApplePay && Model.Placement != ButtonPlacement.Product).ToString().ToLower());
        if (useApplePay && (!isCartPage || $('#checkout').length > 0)) {
            if (window.ApplePaySession) {
                if (ApplePaySession.canMakePayments()) {
                    //get merchant configs and render the button
                    paypal.Applepay().config().then(config => {
                        applePayConfig = config;
                        if (applePayConfig.isEligible) {
                            $('#@paypalApplePayContainer').html('<apple-pay-button id="btn-appl" onclick="applePayClicked()" buttonstyle="black" type="check-out">');
                            $('#@paypalButtonContainer').after($('#@paypalApplePayContainer'));
                        }
                    });
                } else {
                    console.error('This device is not capable of making Apple Pay payments');
                }
            } else {
                console.error('This device does not support Apple Pay');
            }
        }

        //render Google Pay button
        var useGooglePay = @settings.UseGooglePay.ToString().ToLower();
        if (useGooglePay && google && paypal.Googlepay && (!isCartPage || $('#checkout').length > 0)) {
            onGooglePayLoaded();
        }
    });

    function changeSavedCard(value) {
        if (value > 0) {
            $('#card-number-field-container').hide();
            $('#card-expiry-field-container').hide();
            $('#card-cvv-field-container').hide();
            $('#card-name-field-container').hide();
            $('#card-vault').hide();
        } else {
            $('#card-number-field-container').show();
            $('#card-expiry-field-container').show();
            $('#card-cvv-field-container').show();
            $('#card-name-field-container').show();
            $('#card-vault').show();
        }
    }

    function initCallback(data, actions) {
        //add appropriate classes for containers when initialize buttons
        if (isCartPage) {
            $('#@paypalButtonContainer').addClass('paypal-cart');
            if ($('#@paypalApplePayContainer').length > 0) {
                $('#@paypalApplePayContainer').addClass('paypal-cart');
            }
            if ($('#@paypalGooglePayContainer').length > 0) {
                $('#@paypalGooglePayContainer').addClass('paypal-cart');
            }
        }
        if (isProductPage) {
            $('#@paypalButtonContainer').addClass('paypal-product');
            if ($('#@paypalApplePayContainer').length > 0) {
                $('#@paypalApplePayContainer').addClass('paypal-product');
            }
            if ($('#@paypalGooglePayContainer').length > 0) {
                $('#@paypalGooglePayContainer').addClass('paypal-product');
            }
            if ($('#@paypalMessagesContainer').length > 0) {
                $('#@paypalMessagesContainer').addClass('paypal-product');
            }
        }
        if (isPaymentMethodPage) {
            $('#@paypalButtonContainer').addClass('paypal-checkout');
            if ($('#@paypalApmContainer').length > 0) {
                $('#@paypalApmContainer').addClass('paypal-checkout');
            }
            if ($('#@paypalCardContainer').length > 0) {
                $('#@paypalCardContainer').addClass('paypal-checkout');
            }
            if ($('#@paypalApplePayContainer').length > 0) {
                $('#@paypalApplePayContainer').addClass('paypal-checkout');
            }
            if ($('#@paypalGooglePayContainer').length > 0) {
                $('#@paypalGooglePayContainer').addClass('paypal-checkout');
            }
        }
    }

    function clickCallback(data, actions) {
        var cancel = false;
        if (isCartPage) {
            //check terms of services in the cart page
            if ($('#termsofservice').length > 0 && !$('#termsofservice').is(':checked')) {
                $('#terms-of-service-warning-box').dialog();
                cancel = true;
            } else {
                if (waitingPayPal !== false)
                    return actions.reject();

                displayAjaxLoading(true);
                waitingPayPal = true;

                //validate shopping cart
                $.ajax({
                    cache: false,
                    async: false,
                    type: 'GET',
                    url: '@(Url.Action("ValidateShoppingCart", "PayPalCommercePublic"))',
                    success: function (data, textStatus, jqXHR) {
                        if (data.error) {
                            displayBarNotification(data.error, 'error', 0);
                            cancel = true;
                        } else {
                            cancel = false;
                        }
                    },
                    complete: function (jqXHR, textStatus) {
                        displayAjaxLoading(false);
                        waitingPayPal = false;
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        if (errorThrown) {
                            displayBarNotification(errorThrown, 'error', 0);
                        } else {
                            displayBarNotification(textStatus, 'error', 0);
                        }
                        cancel = true;
                    }
                });
            }
        }

        //on product pages when customer clicks the button we should first add the product in the cart
        if (isProductPage && $('#add-to-cart-button-@Model.ProductId').length > 0) {
            if (waitingPayPal !== false)
                return actions.reject();

            displayAjaxLoading(true);
            waitingPayPal = true;

            $.ajax({
                async: false,
                type: 'POST',
                url: '@Url.RouteUrl("AddProductToCart-Details", new { productId = Model.ProductId, shoppingCartTypeId = (int)ShoppingCartType.ShoppingCart })',
                data: $('#product-details-form').serialize(),
                success: function (response, textStatus, jqXHR) {
                    if (response.success || response.redirect) {
                        //validate shopping cart
                        $.ajax({
                            cache: false,
                            async: false,
                            type: 'GET',
                            url: '@(Url.Action("ValidateShoppingCart", "PayPalCommercePublic"))',
                            success: function (data, textStatus, jqXHR) {
                                if (data.error) {
                                    displayBarNotification(data.error, 'error', 0);
                                    cancel = true;
                                } else {
                                    cancel = false;
                                }
                            },
                            error: function (jqXHR, textStatus, errorThrown) {
                                if (errorThrown) {
                                    displayBarNotification(errorThrown, 'error', 0);
                                } else {
                                    displayBarNotification(textStatus, 'error', 0);
                                }
                                cancel = true;
                            }
                        });
                    } else {
                        if (response.message) {
                            displayBarNotification(response.message, 'error', 0);
                        }
                        cancel = true;
                    }
                },
                complete: function (jqXHR, textStatus) {
                    displayAjaxLoading(false);
                    waitingPayPal = false;
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    if (errorThrown) {
                        displayBarNotification(errorThrown, 'error', 0);
                    } else {
                        displayBarNotification(textStatus, 'error', 0);
                    }
                    cancel = true;
                }
            });
        }

        //on the checkout page display alternative payment methods if any
        if (isPaymentMethodPage) {
            if (!data.fundingSource || data.fundingSource == 'paypal' || data.fundingSource == 'card' || data.fundingSource == 'applepay' || data.fundingSource == 'googlepay' || data.fundingSource == 'paylater' || data.fundingSource == 'venmo') {
                isApm = false;
                cancel = false;
            } else if (isApm) {
                cancel = false;
            } else {
                paypalButtonContainer = null;
                $('#@paypalButtonContainer').html('');

                //render APM fields
                paypal.PaymentFields({
                    fundingSource: data.fundingSource,
                    fields: {
                        name: {
                            value: '@Model.Customer.FullName',
                        },
                        email: {
                            value: '@Model.Customer.Email',
                        },
                    }
                }).render('#@paypalApmContainer');

                paypalButtonContainer = paypal.Buttons({
                    fundingSource: data.fundingSource,
                    style: {
                        label: 'pay'
                    },
                    onClick: clickCallback,
                    createOrder: createOrderCallback,
                    onApprove: approveCallback,
                    onCancel: cancelCallback,
                    onError: errorCallback
                });
                paypalButtonContainer.render('#@paypalButtonContainer')
                if ($('#@paypalApmContainer').length > 0) {
                    $('#@paypalApmContainer').after($('#@paypalButtonContainer'));
                }
                isApm = true;

                cancel = true;
            }
        }

        if (cancel === true) {
            return actions.reject();
        } else {
            return actions.resolve();
        }
    }

    function createOrderCallback(data, actions) {
        var orderId = '';
        var status = '';
        if (waitingPayPal !== false)
            throw new Error('Request in processing');

        displayAjaxLoading(true);
        waitingPayPal = true;

        //create order
        var postData = {
            placement: '@((int)Model.Placement)',
            paymentSource: data.paymentSource,
            cardId: data.cardId,
            saveCard: $('#vault').length > 0 && $('#vault').is(':checked')
        };
        addAntiForgeryToken(postData);
        $.ajax({
            async: false,
            type: 'POST',
            url: '@(Url.Action("CreateOrder", "PayPalCommercePublic"))',
            data: postData,
            success: function (data, textStatus, jqXHR) {
                if (data.error) {
                    displayBarNotification(data.error, 'error', 0);
                } else if (data.redirect) {
                    setLocation(data.redirect);
                } else {
                    orderId = data.orderId;
                    status = data.status;
                }
            },
            complete: function (jqXHR, textStatus) {
                displayAjaxLoading(false);
                waitingPayPal = false;
            },
            error: function (jqXHR, textStatus, errorThrown) {
                if (errorThrown) {
                    displayBarNotification(errorThrown, 'error', 0);
                } else {
                    displayBarNotification(textStatus, 'error', 0);
                }
            }
        });

        return orderId;
    }

    function approveCallback(data, actions) {
        var redirectUrl = '';

        //check liability shift
        if (data.liabilityShift === 'NO') {
            displayBarNotification('3D Secure contingency is not resolved', 'error', 0);
            return;
        }
        displayAjaxLoading(true);
        waitingPayPal = true;

        //approve order and redirect customer to the confirmation page
        var postData = {
            orderId: data.orderID ?? data.orderId,
            liabilityShift: data.liabilityShift
        };
        addAntiForgeryToken(postData);
        $.ajax({
            async: false,
            type: 'POST',
            url: '@(Url.Action("ApproveOrder", "PayPalCommercePublic"))',
            data: postData,
            success: function (data, textStatus, jqXHR) {
                if (data.redirect) {
                    redirectUrl = data.redirect;
                } else {
                    displayBarNotification(data.error, 'error', 0);
                    redirectUrl = '@Url.RouteUrl(PayPalCommerceDefaults.Route.ShoppingCart)';
                }
            },
            complete: function (jqXHR, textStatus) {
                displayAjaxLoading(false);
                waitingPayPal = false;
            },
            error: function (jqXHR, textStatus, errorThrown) {
                if (errorThrown) {
                    displayBarNotification(errorThrown, 'error', 0);
                } else {
                    displayBarNotification(textStatus, 'error', 0);
                }
            }
        });
        setLocation(redirectUrl);
    }

    function cancelCallback(data) {
        if (isProductPage) {
            setLocation('@Url.RouteUrl(PayPalCommerceDefaults.Route.ShoppingCart)');
        }
    }

    function errorCallback(err) {
        displayBarNotification(err.message, 'error', 0);
    }

    function shippingAddressChangeCallback(data, actions) {
        //shipping address was changed on the PayPal's side, we should update it here as well
        if (!isPaymentMethodPage) {
            var postData = {
                orderId: data.orderID ?? data.orderId,
                paymentId: data.paymentID,
                paymentToken: data.paymentToken,
                addressCity: data.shippingAddress.city,
                addressState: data.shippingAddress.state,
                addressCountryCode: data.shippingAddress.countryCode,
                addressPostalCode: data.shippingAddress.postalCode
            };
            addAntiForgeryToken(postData);
            $.ajax({
                async: false,
                type: 'POST',
                url: '@(Url.Action("UpdateOrderShipping", "PayPalCommercePublic"))',
                data: postData,
                success: function (data, textStatus, jqXHR) {
                    if (data.error) {
                        actions.reject(data.errors.ADDRESS_ERROR);
                    }
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    actions.reject(data.errors.ADDRESS_ERROR);
                }
            });
        }
    }

    function shippingOptionsChangeCallback(data, actions) {
        //shipping option was selected on the PayPal's side, we should update it here as well
        if (!isPaymentMethodPage) {
            var postData = {
                orderId: data.orderID ?? data.orderId,
                paymentId: data.paymentID,
                paymentToken: data.paymentToken,
                optionId: data.selectedShippingOption.id,
                optionType: data.selectedShippingOption.type,
                optionLabel: data.selectedShippingOption.label,
                optionSelected: data.selectedShippingOption.selected,
                optionAmount: data.selectedShippingOption.amount.value
            };
            addAntiForgeryToken(postData);
            $.ajax({
                async: false,
                type: 'POST',
                url: '@(Url.Action("UpdateOrderShipping", "PayPalCommercePublic"))',
                data: postData,
                success: function (data, textStatus, jqXHR) {
                    if (data.error) {
                        actions.reject(data.errors.METHOD_UNAVAILABLE);
                    }
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    actions.reject(data.errors.METHOD_UNAVAILABLE);
                }
            });
        }
    }

    function applePayClicked() {
        //get transaction info required to start Apple Pay session
        var applePayRequest = null;
        var postData = {
            placement: '@((int)Model.Placement)'
        };
        addAntiForgeryToken(postData);
        $.ajax({
            async: false,
            type: 'POST',
            url: '@(Url.Action("AppleTransactionInfo", "PayPalCommercePublic"))',
            data: postData,
            success: function (data, textStatus, jqXHR) {
                if (data.error) {
                    displayBarNotification(data.error, 'error', 0);
                } else {
                    applePayRequest = {
                        countryCode: applePayConfig.countryCode,
                        merchantCapabilities: applePayConfig.merchantCapabilities,
                        supportedNetworks: applePayConfig.supportedNetworks,
                        currencyCode: data.transactionInfo.currencyCode,
                        billingContact: data.transactionInfo.billingContact,
                        shippingContact: data.transactionInfo.shippingContact,
                        shippingMethods: data.transactionInfo.shippingMethods,
                        shippingType: data.transactionInfo.shippingType,
                        shippingContactEditingMode: data.transactionInfo.shippingContactEditingMode,
                        requiredShippingContactFields: ['name', 'email', 'postalAddress'],
                        requiredBillingContactFields: ['postalAddress'],
                        total: data.transactionInfo.total,
                        lineItems: data.transactionInfo.lineItems
                    };
                }
            },
            error: function (jqXHR, textStatus, errorThrown) {
                if (errorThrown) {
                    displayBarNotification(errorThrown, 'error', 0);
                } else {
                    displayBarNotification(textStatus, 'error', 0);
                }
            }
        });

        //create new session
        var session = new ApplePaySession(4, applePayRequest);
        session.onvalidatemerchant = (event) => {
            paypal.Applepay().validateMerchant({
                validationUrl: event.validationURL,
                displayName: applePayRequest.total.label
            }).then(validateResult => {
                session.completeMerchantValidation(validateResult.merchantSession);
            }).catch(validateError => {
                console.error(validateError);
                session.abort();
            });
        };
        session.onpaymentauthorized = (event) => {
            //payment has been authorized, so create a new order
            var orderId = createOrderCallback({ paymentSource: 'applepay' });
            paypal.Applepay().confirmOrder({ orderId: orderId, token: event.payment.token, billingContact: event.payment.billingContact }).then(success => {
                //and approve it then
                session.completePayment(ApplePaySession.STATUS_SUCCESS);
                approveCallback({ orderID: orderId, liabilityShift: 'YES' });
            }).catch(error => {
                if (error) {
                    console.error(error);
                    session.completePayment(ApplePaySession.STATUS_FAILURE);
                }
            });
        };
        session.onshippingmethodselected = (event) => {
            var updatedData = null;
            var postData = {
                placement: '@((int)Model.Placement)',
                optionId: event.shippingMethod.identifier
            };
            addAntiForgeryToken(postData);
            $.ajax({
                async: false,
                type: 'POST',
                url: '@(Url.Action("UpdateAppleShipping", "PayPalCommercePublic"))',
                data: postData,
                success: function (data, textStatus, jqXHR) {
                    if (data.error) {
                        updatedData = { status: ApplePaySession.STATUS_FAILURE, total: null, lineItems: null };
                    } else {
                        updatedData = { status: ApplePaySession.STATUS_SUCCESS, total: data.total, lineItems: data.lineItems };
                    }
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    updatedData = { status: ApplePaySession.STATUS_FAILURE, total: null, lineItems: null };
                }
            });
            session.completeShippingMethodSelection(updatedData.status, updatedData.total, updatedData.lineItems);
        };
        session.onshippingcontactselected = (event) => {
            var updatedData = null;
            var postData = {
                placement: '@((int)Model.Placement)',
                addressCity: event.shippingContact.locality,
                addressState: event.shippingContact.administrativeArea,
                addressCountryCode: event.shippingContact.countryCode,
                addressPostalCode: event.shippingContact.postalCode
            };
            addAntiForgeryToken(postData);
            $.ajax({
                async: false,
                type: 'POST',
                url: '@(Url.Action("UpdateAppleShipping", "PayPalCommercePublic"))',
                data: postData,
                success: function (data, textStatus, jqXHR) {
                    if (data.error) {
                        updatedData = { status: ApplePaySession.STATUS_FAILURE, shippingMethods: null, total: null, lineItems: null };
                    } else {
                        updatedData = { status: ApplePaySession.STATUS_SUCCESS, shippingMethods: data.shippingMethods, total: data.total, lineItems: data.lineItems };
                    }
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    updatedData = { status: ApplePaySession.STATUS_FAILURE, shippingMethods: null, total: null, lineItems: null };
                }
            });
            session.completeShippingContactSelection(updatedData.status, updatedData.shippingMethods, updatedData.total, updatedData.lineItems);
        };

        //begin session when customer clicks the button
        clickCallback({ fundingSource: 'applepay' }, {
            resolve: function () {
                session.begin();
            },
            reject: function () {
                return;
            }
        });
    }

    function getGooglePayClient() {
        if (googlePayClient) {
            return googlePayClient;
        }

        //check whether the shipping is requied, we need it to properly create the client
        var shippingIsRequired = false;
        var postData = {
            placement: '@((int)Model.Placement)',
            productId: @(Model.ProductId?.ToString() ?? "null"),
        };
        addAntiForgeryToken(postData);
        $.ajax({
            async: false,
            type: 'POST',
            url: '@(Url.Action("CheckGoogleShipping", "PayPalCommercePublic"))',
            data: postData,
            success: function (data, textStatus, jqXHR) {
                if (data.error) {
                    displayBarNotification(data.error, 'error', 0);
                } else {
                    shippingIsRequired = data.shippingIsRequired;
                }
            },
            error: function (jqXHR, textStatus, errorThrown) {
                if (errorThrown) {
                    displayBarNotification(errorThrown, 'error', 0);
                } else {
                    displayBarNotification(textStatus, 'error', 0);
                }
            }
        });

        //create a new client
        var paymentOptions = {
            environment: '@(settings.UseSandbox ? "TEST" : "PRODUCTION")',
            paymentDataCallbacks: shippingIsRequired === true
                ? { onPaymentAuthorized: googlePaymentAuthorized, onPaymentDataChanged: googlePaymentDataChanged }
                : { onPaymentAuthorized: googlePaymentAuthorized }
        };
        googlePayClient = new google.payments.api.PaymentsClient(paymentOptions);
        return googlePayClient;
    }

    async function getGooglePayConfig() {
        if (!googlePayConfig) {
            googlePayConfig = await paypal.Googlepay().config();
        }
        var allowedPaymentMethods = googlePayConfig.allowedPaymentMethods;
        var merchantInfo = googlePayConfig.merchantInfo;
        return { allowedPaymentMethods, merchantInfo };
    }

    async function onGooglePayLoaded() {
        var { allowedPaymentMethods } = await getGooglePayConfig();
        var isReadyToPayRequest = { apiVersion: 2, apiVersionMinor: 0, allowedPaymentMethods: allowedPaymentMethods };
        var paymentsClient = getGooglePayClient();
        paymentsClient.isReadyToPay(isReadyToPayRequest).then(response => {
            if (response.result) {
                addGooglePayButton();
            }
        });
    }

    function addGooglePayButton() {
        //render the button
        var buttonOptions = { onClick: googlePayButtonClicked, buttonType: 'pay', buttonSizeMode: 'fill' };
        var paymentsClient = getGooglePayClient();
        var button = paymentsClient.createButton(buttonOptions);

        $('#@paypalGooglePayContainer').html(button);
        $('#@paypalButtonContainer').after($('#@paypalGooglePayContainer'));
    }

    async function googlePayButtonClicked() {
        var { allowedPaymentMethods, merchantInfo } = await getGooglePayConfig();
        clickCallback({ fundingSource: 'googlepay' }, {
            resolve: function () {
                //get transaction info required to handle payment request
                var postData = {
                    placement: '@((int)Model.Placement)'
                };
                addAntiForgeryToken(postData);
                $.ajax({
                    async: false,
                    type: 'POST',
                    url: '@(Url.Action("GoogleTransactionInfo", "PayPalCommercePublic"))',
                    data: postData,
                    success: function (data, textStatus, jqXHR) {
                        if (data.error) {
                            displayBarNotification(data.error, 'error', 0);
                        } else if (data.redirect) {
                            setLocation(data.redirect);
                        } else {
                            var paymentDataRequest = {
                                apiVersion: 2,
                                apiVersionMinor: 0,
                                allowedPaymentMethods: allowedPaymentMethods,
                                merchantInfo: merchantInfo,
                                callbackIntents: data.callbacks,
                                transactionInfo: data.transactionInfo,
                                shippingAddressRequired: data.shipping,
                                shippingOptionRequired: data.shipping,
                                shippingAddressParameters: { phoneNumberRequired: false }
                            };
                            var paymentsClient = getGooglePayClient();
                            paymentsClient.loadPaymentData(paymentDataRequest);
                        }
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        if (errorThrown) {
                            displayBarNotification(errorThrown, 'error', 0);
                        } else {
                            displayBarNotification(textStatus, 'error', 0);
                        }
                    }
                });
            }, reject: function () {
                return;
            }
        });
    }

    function googlePaymentDataChanged(intermediatePaymentData) {
        return new Promise(function (resolve, reject) {
            var paymentDataRequestUpdate = {};
            var shippingAddressError = { reason: "SHIPPING_ADDRESS_UNSERVICEABLE", message: "Cannot ship to the selected address", intent: "SHIPPING_ADDRESS" };
            var shippingOptionError = { reason: "SHIPPING_OPTION_INVALID", message: "Shipping option unavailable", intent: "SHIPPING_OPTION" };
            if (intermediatePaymentData.callbackTrigger == 'INITIALIZE' || intermediatePaymentData.callbackTrigger == 'SHIPPING_ADDRESS') {
                var postData = {
                    placement: '@((int)Model.Placement)',
                    addressCity: intermediatePaymentData.shippingAddress.locality,
                    addressState: intermediatePaymentData.shippingAddress.administrativeArea,
                    addressCountryCode: intermediatePaymentData.shippingAddress.countryCode,
                    addressPostalCode: intermediatePaymentData.shippingAddress.postalCode
                };
                addAntiForgeryToken(postData);
                $.ajax({
                    async: false,
                    type: 'POST',
                    url: '@(Url.Action("UpdateGoogleShipping", "PayPalCommercePublic"))',
                    data: postData,
                    success: function (data, textStatus, jqXHR) {
                        if (data.error) {
                            paymentDataRequestUpdate.error = shippingAddressError;
                        } else {
                            paymentDataRequestUpdate.newShippingOptionParameters = data.options;
                            paymentDataRequestUpdate.newTransactionInfo = data.transactionInfo;
                        }
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        paymentDataRequestUpdate.error = shippingAddressError;
                    }
                });
            } else if (intermediatePaymentData.callbackTrigger == 'SHIPPING_OPTION') {
                var postData = {
                    placement: '@((int)Model.Placement)',
                    optionId: intermediatePaymentData.shippingOptionData.id
                };
                addAntiForgeryToken(postData);
                $.ajax({
                    async: false,
                    type: 'POST',
                    url: '@(Url.Action("UpdateGoogleShipping", "PayPalCommercePublic"))',
                    data: postData,
                    success: function (data, textStatus, jqXHR) {
                        if (data.error) {
                            paymentDataRequestUpdate.error = shippingOptionError;
                        } else {
                            paymentDataRequestUpdate.newTransactionInfo = data.transactionInfo;
                        }
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        paymentDataRequestUpdate.error = shippingOptionError;
                    }
                });
            }
            resolve(paymentDataRequestUpdate);
        });
    }

    function googlePaymentAuthorized(paymentData) {
        return new Promise(function (resolve, reject) {
            processPayment(paymentData).then(data => {
                resolve({ transactionState: 'SUCCESS' });
            }).catch(error => {
                resolve({ transactionState: 'ERROR' });
            });
        });
    }

    async function processPayment(paymentData) {
        try {
            //payment has been authorized, so create a new order
            var orderId = createOrderCallback({ paymentSource: 'googlepay' });
            var { status } = await paypal.Googlepay().confirmOrder({
                orderId: orderId,
                paymentMethodData: paymentData.paymentMethodData,
                shippingAddress: paymentData.shippingAddress
            });

            //then approve it immediately or request 3DS verification
            if (status === 'APPROVED' || status === 'COMPLETED') {
                approveCallback({ orderID: orderId, liabilityShift: 'YES' });
            } else if (status === 'CREATED' || status === 'PAYER_ACTION_REQUIRED') {
                paypal.Googlepay().initiatePayerAction({ orderId: orderId }).then(data => {
                    approveCallback({ orderID: orderId, liabilityShift: data.liabilityShift });
                });
            }
        } catch (err) {
            return { transactionState: 'ERROR', error: { message: err.message, } };
        }
    }

</script>